Imports
library(AlphaSimR)
library(devtools)
library(dplyr)
library(ggplot2)
library(factoextra)
library(patchwork)
library(plotly)
library(purrr)
library(reshape2)
library(snow)
library(tibble)
library(tidyr)
library(viridis)
rm(list = ls())
set.seed(123)
source("Functions/Fitness.R")
source("Functions/TraitArchitecture.R")
source("Scripts/GlobalVariables.R")
source("Scripts/CreateFounderPop.R")
output_dir <- file.path(getwd(), "Output")
if (!dir.exists(output_dir)) dir.create(output_dir)
View a fitness landscape
p <- plotFitnessLandscape()
save_dir <- file.path(output_dir, "FitnessFunctions")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
fname <- file.path(save_dir, "fitness_function.html")
htmlwidgets::saveWidget(as_widget(p), fname)
Plot different trait architectures according to different
algorithms
save_dir <- file.path(output_dir, "TraitArchitecture")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
# Additive effects
methodType <- "Additive"
g <- plotTraitArchitecture(founderPop, methodType)
ggplot2::ggsave(filename = paste0("traitarchitecture_", methodType, ".pdf"),
path=save_dir,
device = "pdf")
# Effect of each allele on fitness
methodType <- "Fitness"
g <- plotTraitArchitecture(founderPop, methodType)
ggplot2::ggsave(filename = paste0("traitarchitecture_", methodType, ".pdf"),
path=save_dir,
device = "pdf")
p <- plot3dPopulationFitness(founderPop, calculateFitnessTwoTrait)
fname <- file.path(save_dir, "3dpopulationfitness.html")
htmlwidgets::saveWidget(as_widget(p), fname)
Simulate several adaptive walks with different population sizes
# Different starting population sizes
n.popSizes <- c(40,200,1000)
# Don't use a SNP chip for these simulations
addSnpChip <- FALSE
fig <- plot_ly()
fit_df <- data.frame(gen=1:n.gens,
fitness=numeric(n.gens),
traitValA=numeric(n.gens),
traitValB=numeric(n.gens))
# Iterate through each population size
for (p in c(1:length(n.popSizes))) {
n.popSize <- n.popSizes[p]
print(n.popSize)
# Create a new population of that size
source("Scripts/CreateFounderPop.R")
pop <- founderPop
# Iterate through the generations, update the result dataframe, and advance
# progeny based on the two trait fitness funcion
for(gen in 1:n.gens) {
fit_df$fitness[gen] <- mean(twoTraitFitFunc(gv(pop)))
fit_df$traitValA[gen] <- meanG(pop)[1]
fit_df$traitValB[gen] <- meanG(pop)[2]
pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=n.popSize*(n.selProp), nCrosses=n.popSize)
}
# Add a trace to represent this population's adaptive walk
fig <- add_trace(
fig,
fit_df,
name = n.popSize,
x = fit_df$traitValA,
y = fit_df$traitValB,
z = fit_df$fitness,
type = 'scatter3d',
mode = 'lines',
opacity = 1,
color = p,
line = list(width = 5)
)
}
[1] 40
[1] 200
[1] 1000
fig <- fig %>%
layout(legend=list(title=list(text='Population Size')),
scene = list(xaxis = list(title = "Trait A"),
yaxis = list(title = "Trait B"),
zaxis = list(title = "Fitness"),
aspectmode='cube')) %>% hide_colorbar()
save_dir <- file.path(output_dir, "DifferentPopulationSizes")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
fname <- file.path(save_dir, paste0("populationSizes_", n.popSizes[1], ",", n.popSizes[2], ",", n.popSizes[3], ".html"))
htmlwidgets::saveWidget(as_widget(fig), fname)
# Reset variables
source("Scripts/CreateFounderPop.R")
source("Scripts/GlobalVariables.R")
Overlay an adaptive walk over a fitness landscape, and save 3D and 2D
versions
fig <- plot_ly()
fit_df <- data.frame(gen=1:n.gens,
fitness=numeric(n.gens),
traitValA=numeric(n.gens),
traitValB=numeric(n.gens))
pop <- founderPop
for(gen in 1:n.gens) {
fit_df$fitness[gen] <- mean(twoTraitFitFunc(gv(pop)))
fit_df$traitValA[gen] <- meanG(pop)[1]
fit_df$traitValB[gen] <- meanG(pop)[2]
meanFitness <- mean(twoTraitFitFunc(pheno(pop)))
selRat <- selectionRatio(meanFitness)
pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=n.popSize*selRat, nCrosses=n.popSize)
}
save_dir <- file.path(output_dir, "OverlayAdaptiveWalk")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
plotType <- "CONTOUR"
pc <- overlayWalkOnLandscape(fit_df, type=plotType, calculateFitnessTwoTrait)
fname <- file.path(save_dir, paste0("adaptivewalk_", plotType, ".html"))
htmlwidgets::saveWidget(as_widget(pc), fname)
plotType <- "SURFACE"
ps <- overlayWalkOnLandscape(fit_df, type=plotType, calculateFitnessTwoTrait)
fname <- file.path(save_dir, paste0("adaptivewalk_", plotType, ".html"))
htmlwidgets::saveWidget(as_widget(ps), fname)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
Plot the change in allele frequency over time
save_dir <- file.path(output_dir, "AlleleFrequencies")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
ggplot2::ggsave(filename = paste0("allelefrequencies.pdf"),
path=save_dir,
device = "pdf")
Saving 7 x 7 in image
Simulate an adaptive walk, and plot the following curve: #segregating
QTL/#segregating loci
Original question: out of segregating alleles left in the population,
how many of them would increase fitness? Is this even a valid
question?
TODO: - How does this change with size of allele? - How does size of
‘fitness delta’ change over generations? - Simualate mutations for a
single individual to figure out P(allelic substitution is favorable),
should reflect FKO - P(mutation gets fixed) - show that this matches
Kimura - Compare adaptive walks for DH/inbred population where there are
new mutations VS population w/standing genetic variation
n.gens <- 200
# Reset variables
source("Scripts/CreateFounderPop.R")
source("Scripts/GlobalVariables.R")
pop <- founderPop
df <- data.frame(gen=c(),
fitness=c(),
traitVal1=c(),
traitVal2=c(),
percFitInc=c(),
segAlleles=c(),
segQtl=c())
# Iterate through the generations
for (gen in 1:n.gens) {
print(gen)
# Counter variables
numHetLoci = 0
numHetQtl = 0
numInc = 0
# Get all of the loci from the population
segSiteGeno <- pullSegSiteGeno(pop)
# Get all of the QTL from the population (a sampling of the segSiteGeno)
qtlGeno <- getUniqueQtl(pop)
qtlLoci <- colnames(qtlGeno)
loci <- colnames(segSiteGeno)
nLoci <- ncol(segSiteGeno)
# Iterate through all of the loci
# TODO: just calculate numHetQtl / numHetLoci
for (l in 1:ncol(segSiteGeno)) {
id <- loci[l]
locus = segSiteGeno[,l]
# Check if the locus is segregating
if (hetLocus(locus)) {
# Increment the counter
numHetLoci <- numHetLoci + 1
# Determine the effect size - this is where the bug is
#e <- getEffectSize(locus, id, pop, "Fitness")
#if (e > 0) {
# numInc <- numInc +1
#}
# If the locus is a QTL, then it has an effect size
if (id %in% qtlLoci) {
numHetQtl <- numHetQtl + 1
}
}
}
# Calculate the ratio of segregating QTL to segregating alleles
if (numHetLoci == 0) {
perc <- 0
} else {
perc <- (numHetQtl / numHetLoci)
}
df <- rbind(df, data.frame(gen=gen,
fitness=mean(twoTraitFitFunc(gv(pop))),
traitVal1=meanG(pop)[1],
traitVal2=meanG(pop)[2],
percFitInc=perc,
segAlleles=numHetLoci,
segQtl=numHetQtl))
# If the population is within n.margin, terminate the simulation
if (mean(twoTraitFitFunc(gv(pop))) >= n.margin) {
break
}
meanFitness <- mean(twoTraitFitFunc(pheno(pop)))
selRat <- selectionRatio(meanFitness)
# Advance the top progeny according to the fitness function
pop <- selectCross(pop=pop, trait=twoTraitFitFunc, nInd=nInd(pop)*selRat, nCrosses=nInd(pop))
}
save_dir <- file.path(output_dir, "BeneficialAlleles")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
fname <- file.path(save_dir, "beneficialalleles.pdf")
pdf(fname)
# Plot the ratio of segregating QTL to segregating alleles, along with fitness
par(mar = c(5, 5, 3, 5) + 0.3)
plot(df$gen, df$fitness, type="l", lwd = "3", col=2, xlab = "Generation", ylab = "Fitness")
par(new = TRUE)
plot(df$gen, df$percFitInc, type="l", lwd = "3", col = 3, axes = FALSE, xlab = "", ylab = "")
axis(side = 4, at = pretty(range(df$percFitInc)))
mtext("% of Segregating alleles that are QTL", side = 4, line = 3)
par(xpd=TRUE)
legend("right",
c("Fitness", "% Alleles"),
lty = 1,
lwd = 3,
col = 2:3)
dev.off()
This block runs n.nPops * n.sims Monte Carlo simulations of different
populations to determine the change in the average effect size of
alleles that are fixed along the adaptive walk, saving the order in
which the alleles are fixed n.nPops populations are created, and each
one undergoes n.sims unique adaptive walks
Right now, we do see an upward curve for the additive effect chart. A
favored hypothesis: - Most of the alleles have a small effect size, so
it is more likely that they would be fixed
n.sims <- 10
n.popResets <- 10
n.selProp <- 0.3
n.qtlPerChr <- 5
addSnpChip <- FALSE
fig <- plot_ly()
# Tidy dataframe to store the order in which each allele is fixed, and the effect size
eff_size_df <- data.frame(orderFixed=c(),
effectSizeA=c(),
effectSizeF=c())
# Create a new population n.nPops times
for (p in 1:n.popResets) {
print(paste0("Pop Reset: ", p))
source("Scripts/CreateFounderPop.R")
for (s in 1:n.sims) {
pop <- founderPop
pop_df <- data.frame(gen=1:n.gens,
fitness=numeric(n.gens),
traitValA=numeric(n.gens),
traitValB=numeric(n.gens))
print(paste0("Sim: ", s))
# idx is the order in which an allele is fixed along an adaptive walk
idx <- 1
# whether or not to increment the idx counter. Multiple alleles may be fixed
# in the same generation, so this cannot be incremented until each locus
# has been examined
inc <- FALSE
# Burn-in
for (gen in 1:n.burnInGens) {
pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=nInd(pop)*n.burnInSelProp, nCrosses=nInd(pop))
}
# Main simulation
for (gen in 1:n.gens) {
meanFitness <- mean(twoTraitFitFunc(pheno(pop)))
selRat <- selectionRatio(meanFitness)
pop_df$fitness[gen] <- meanFitness
pop_df$traitValA[gen] <- meanP(pop)[1]
pop_df$traitValB[gen] <- meanP(pop)[2]
# Keep track of the current population
prevPop <- pop
# Advance the population based on fitness
pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=nInd(pop)*selRat, nCrosses=nInd(pop))
# Get the qtl genotypes from the current and new populations, so we can compare them
prevGeno <- getUniqueQtl(prevPop)
newGeno <- getUniqueQtl(pop)
cols <- colnames(newGeno)
n.loci <- length(cols)
# Iterate through all loci
for (l in 1:n.loci) {
id <- cols[l]
prevLocus = prevGeno[,l]
newLocus = newGeno[,l]
# Check if the allele was segregating and became fixed in this generation
if (hetLocus(prevLocus) && !hetLocus(newLocus)) {
# Increment the order counter after this generation
inc <- TRUE
# Determine the effect size, based on additivity
effSizeA <- getEffectSize(prevLocus,
id,
prevPop,
"Additive")
#print(paste0("Gen: ", gen, ", Order: ", idx, ", Loc: ", id, ": ", effSizeA)) # REMOVE
# Determine the effect size based on fitness
effSizeF <- getEffectSize(prevLocus,
id,
prevPop,
"Fitness")
# Update the result dataframe
new_row <- data.frame(orderFixed=c(idx),
effectSizeA=c(effSizeA),
effectSizeF=c(effSizeF))
eff_size_df <- rbind(eff_size_df, new_row)
}
}
# Check whether to increment the order counter and reset 'inc'
if (inc) {
idx <- idx + 1
inc <- FALSE
}
# If the population is within n.margin, terminate the simulation
if (mean(twoTraitFitFunc(gv(subPop))) >= n.margin) {
break
}
}
# Add the adaptive walk of the sub-population
fig <- add_trace(
fig,
pop_df,
name = s,
x = pop_df$traitValA,
y = pop_df$traitValB,
z = pop_df$fitness,
type = 'scatter3d',
mode = 'lines',
opacity = 1,
color = s,
line = list(width = 2)
)
}
}
save_dir <- file.path(output_dir, "AverageEffectSize")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
# Determine the average additive effect size at each 'step'
grouped_df_a <- eff_size_df %>%
group_by(orderFixed) %>%
summarize(meanEffectSize = mean(effectSizeA))
g_a <- ggplot(data=grouped_df_a, aes(x=orderFixed, y=meanEffectSize)) +
geom_bar(stat="identity") +
geom_smooth(formula = y ~ a^x)
ggplot2::ggsave(filename = "average_effect_size_additive.pdf",
path=save_dir,
device = "pdf")
# Determine the average fitness effect size at each 'step'
grouped_df_f <- eff_size_df %>%
group_by(orderFixed) %>%
summarize(meanEffectSize = mean(effectSizeF))
g_f <- ggplot(data=grouped_df_f, aes(x=orderFixed, y=meanEffectSize)) +
geom_bar(stat="identity") +
geom_smooth(formula = y ~ a^x)
ggplot2::ggsave(filename = "average_effect_size_fitness.pdf",
path=save_dir,
device = "pdf")
# Create a plot with the adaptive walks
p <- fig %>%
layout(legend=list(title=list(text='Population')),
showlegend=FALSE,
scene = list(xaxis = list(title = "Trait A"),
yaxis = list(title = "Trait B"),
zaxis = list(title = "Fitness"),
aspectmode='cube')) %>% hide_colorbar()
fname <- file.path(save_dir, "adaptivewalk.html")
htmlwidgets::saveWidget(as_widget(p), fname)
This block will simulate one base population, from which two
sub-populations are selected, and undergo purifying selection
independently.
source("Scripts/GlobalVariables.R")
n.shape <- 1
n.var <- 0.05
n.qtlPerChr <- 2
n.segSites <- 1000
source("Scripts/CreateFounderPop.R")
n.subPopSize <- 500
n.selRat <- 0.5
n.r <- 0.9
n.gens <- 100
n.margin <- -0.05
plotTraitArchitecture(founderPop)
plot3dPopulationFitness(founderPop, calculateFitnessTwoTrait)
fit_df <- data.frame(gen=1:n.burnInGens,
fitness=numeric(n.burnInGens),
traitValA=numeric(n.burnInGens),
traitValB=numeric(n.burnInGens))
pop <- founderPop
# Burn-in generations
for (gen in 1:n.burnInGens) {
fit_df$fitness[gen] <- mean(twoTraitFitFunc(pheno(pop)))
fit_df$traitValA[gen] <- meanP(pop)[1]
fit_df$traitValB[gen] <- meanP(pop)[2]
pop <- selectCross(pop, trait=twoTraitFitFunc, nInd=nInd(pop)*n.burnInSelProp, nCrosses=nInd(pop))
}
# Create a random vector of size n.pops, with a random order of sub-population ids
randVec <- sample(rep(c(1:n.nPops), times=n.popSize/n.nPops))
# Select all of the "1" indexed individuals
popA <- selectInd(pop, trait=selectSubPop, selectTop=TRUE, nInd=n.subPopSize, idx=1, randVec=randVec)
# Select all of the "2" indexed individuals
popB <- selectInd(pop, trait=selectSubPop, selectTop=TRUE, nInd=n.subPopSize, idx=2, randVec=randVec)
# Create dataframes for each subpopulation, initializing with current values
popA_df <- data.frame(gen=c(1),
fitness=c(mean(twoTraitFitFunc(pheno(popA)))),
traitValA=c(meanP(popA)[1]),
traitValB=c(meanP(popA)[2]))
popB_df <- data.frame(gen=c(1),
fitness=c(mean(twoTraitFitFunc(pheno(popB)))),
traitValA=c(meanP(popB)[1]),
traitValB=c(meanP(popB)[2]))
# Iterate through the generations
for (gen in 1:n.gens) {
# If popA is within the margin of the fitness optimum, don't progress it any further
if (mean(twoTraitFitFunc(pheno(popA))) < n.margin) {
# Advance the population
meanFitness <- mean(twoTraitFitFunc(pheno(popA)))
# Get a selection ratio based on fitness
selRat <- selectionRatio(meanFitness)
popA <- selectCross(popA, trait=twoTraitFitFunc, nInd=nInd(popA)*selRat, nCrosses=nInd(popA))
# Update the dataframe with new values
popA_df <- rbind(popA_df, data.frame(gen=gen,
fitness=meanFitness,
traitValA=meanP(popA)[1],
traitValB=meanP(popA)[2]))
}
# If popB is within the margin of the fitness optimum, don't progress it any further
if (mean(twoTraitFitFunc(pheno(popB))) < n.margin) {
# If popA is within the margin of the fitness optimum, don't progress it any further
meanFitness <- mean(twoTraitFitFunc(pheno(popB)))
# Get a selection ratio based on fitnes
selRat <- selectionRatio(meanFitness)
popB <- selectCross(popB, trait=twoTraitFitFunc, nInd=nInd(popB)*selRat, nCrosses=nInd(popB))
# Update the dataframe with new values
popB_df <- rbind(popB_df, data.frame(gen=gen,
fitness=meanFitness,
traitValA=meanP(popB)[1],
traitValB=meanP(popB)[2]))
}
}
# Update rownames
rownames(popA_df) <- 1:nrow(popA_df)
rownames(popB_df) <- 1:nrow(popB_df)
# Plot the adaptive walks
fig <- plot_ly()
fig <- add_trace(
fig,
fit_df,
name = "Burn In",
x = fit_df$traitValA,
y = fit_df$traitValB,
z = fit_df$fitness,
type = 'scatter3d',
mode = 'lines',
opacity = 1,
color = 'yellow',
line = list(width = 5)
)
fig <- add_trace(
fig,
popA_df,
name = "Pop A",
x = popA_df$traitValA,
y = popA_df$traitValB,
z = popA_df$fitness,
type = 'scatter3d',
mode = 'lines',
opacity = 1,
color = 'red',
line = list(width = 5)
)
fig <- add_trace(
fig,
popB_df,
name = "Pop B",
x = popB_df$traitValA,
y = popB_df$traitValB,
z = popB_df$fitness,
type = 'scatter3d',
mode = 'lines',
opacity = 1,
color = 'blue',
line = list(width = 5)
)
p <- fig %>%
layout(legend=list(title=list(text='Population')),
showlegend=FALSE,
scene = list(xaxis = list(title = "Trait A"),
yaxis = list(title = "Trait B"),
zaxis = list(title = "Fitness"),
aspectmode='cube')) %>% hide_colorbar()
save_dir <- file.path(output_dir, "DivergingPopulations")
if (!dir.exists(save_dir)) dir.create(save_dir)
save_dir <- file.path(save_dir, format(Sys.time(), "%F_%H_%M_%S"))
if (!dir.exists(save_dir)) dir.create(save_dir)
fname <- file.path(save_dir, "adaptivewalks.html")
htmlwidgets::saveWidget(as_widget(p), fname)
write.table(getParams(), file.path(save_dir, "params.txt"), col.names=FALSE, quote=FALSE, sep=":\t")
fname <- file.path(save_dir, "traitarchitecture.pdf")
pdf(fname)
p1 <- plotTraitArchitecture(popA, "Fitness", "popA")
p2 <- plotTraitArchitecture(popB, "Fitness", "popB")
p3 <- plotTraitArchitecture(popA, "Additive", "popA")
p4 <- plotTraitArchitecture(popB, "Additive", "popB")
(p1|p2)/(p3|p4)
dev.off()
quartz_off_screen
2

# Create a density plot of trait 1
trait1.df <- as.data.frame(cbind(pheno(popA)[,1], pheno(popB)[,1]))
colnames(trait1.df) <- c("popA", "popB")
trait1.df <- trait1.df %>%
pivot_longer(c("popA", "popB"), names_to="pop", values_to="pheno")
t1 <- ggplot(trait1.df, aes(pheno, fill=pop, color=pop)) +
geom_density(alpha=0.1) +
labs(title="Trait 1")
# Create a density plot of trait 2
trait2.df <- as.data.frame(cbind(pheno(popA)[,2], pheno(popB)[,2]))
colnames(trait2.df) <- c("popA", "popB")
trait2.df <- trait2.df %>%
pivot_longer(c("popA", "popB"), names_to="pop", values_to="pheno")
t2 <- ggplot(trait2.df, aes(pheno, fill=pop, color=pop)) +
geom_density(alpha=0.1) +
labs(title="Trait 2")
(t1|t2)
ggplot2::ggsave(filename = "trait_distributions.pdf",
path=save_dir,
device = "pdf")
Saving 7.29 x 4.51 in image

plot3dPopulationFitnessTwoPops(popA, popB)
p
LS0tCnRpdGxlOiAiQWRhcHRpdmUgV2Fsa3MiCm91dHB1dDogaHRtbF9ub3RlYm9vawphdXRob3I6IFRlZCBNb255YWsKZGVzY3JpcHRpb246IFRoaXMgbm90ZWJvb2sgY29udGFpbnMgc2NyaXB0cyBmb3IgdW5kZXJzdGFuZGluZyB0aGUgZHluYW1pY3Mgb2YgYWRhcHRpdmUgd2Fsa3MuCi0tLQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRSwgZWNobz1GQUxTRX0KcmVxdWlyZSgia25pdHIiKQpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gIn4vRG9jdW1lbnRzL0NTVS9SL0JyZWVkaW5nU2ltcyIpCmBgYAoKSW1wb3J0cwpgYGB7cn0KbGlicmFyeShBbHBoYVNpbVIpCmxpYnJhcnkoZGV2dG9vbHMpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShmYWN0b2V4dHJhKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShwbG90bHkpCmxpYnJhcnkocHVycnIpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoc25vdykKbGlicmFyeSh0aWJibGUpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkodmlyaWRpcykKcm0obGlzdCA9IGxzKCkpCgpzZXQuc2VlZCgxMjMpCgpzb3VyY2UoIkZ1bmN0aW9ucy9GaXRuZXNzLlIiKQpzb3VyY2UoIkZ1bmN0aW9ucy9UcmFpdEFyY2hpdGVjdHVyZS5SIikKc291cmNlKCJTY3JpcHRzL0dsb2JhbFZhcmlhYmxlcy5SIikKc291cmNlKCJTY3JpcHRzL0NyZWF0ZUZvdW5kZXJQb3AuUiIpCgpvdXRwdXRfZGlyIDwtIGZpbGUucGF0aChnZXR3ZCgpLCAiT3V0cHV0IikKaWYgKCFkaXIuZXhpc3RzKG91dHB1dF9kaXIpKSBkaXIuY3JlYXRlKG91dHB1dF9kaXIpCmBgYAoKVmlldyBhIGZpdG5lc3MgbGFuZHNjYXBlCmBgYHtyfQpwIDwtIHBsb3RGaXRuZXNzTGFuZHNjYXBlKCkKCnNhdmVfZGlyIDwtIGZpbGUucGF0aChvdXRwdXRfZGlyLCAiRml0bmVzc0Z1bmN0aW9ucyIpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCnNhdmVfZGlyIDwtIGZpbGUucGF0aChzYXZlX2RpciwgZm9ybWF0KFN5cy50aW1lKCksICIlRl8lSF8lTV8lUyIpKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsICJmaXRuZXNzX2Z1bmN0aW9uLmh0bWwiKQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChhc193aWRnZXQocCksIGZuYW1lKQpgYGAKClBsb3QgZGlmZmVyZW50IHRyYWl0IGFyY2hpdGVjdHVyZXMgYWNjb3JkaW5nIHRvIGRpZmZlcmVudCBhbGdvcml0aG1zCmBgYHtyfQpzYXZlX2RpciA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIlRyYWl0QXJjaGl0ZWN0dXJlIikKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCBmb3JtYXQoU3lzLnRpbWUoKSwgIiVGXyVIXyVNXyVTIikpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCndyaXRlLnRhYmxlKGdldFBhcmFtcygpLCBmaWxlLnBhdGgoc2F2ZV9kaXIsICJwYXJhbXMudHh0IiksIGNvbC5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UsIHNlcD0iOlx0IikKCiMgQWRkaXRpdmUgZWZmZWN0cwptZXRob2RUeXBlIDwtICJBZGRpdGl2ZSIKZyA8LSBwbG90VHJhaXRBcmNoaXRlY3R1cmUoZm91bmRlclBvcCwgbWV0aG9kVHlwZSkKZ2dwbG90Mjo6Z2dzYXZlKGZpbGVuYW1lID0gcGFzdGUwKCJ0cmFpdGFyY2hpdGVjdHVyZV8iLCBtZXRob2RUeXBlLCAiLnBkZiIpLAogICAgICAgICAgICAgICAgcGF0aD1zYXZlX2RpciwKICAgICAgICAgICAgICAgIGRldmljZSA9ICJwZGYiKQoKIyBFZmZlY3Qgb2YgZWFjaCBhbGxlbGUgb24gZml0bmVzcwptZXRob2RUeXBlIDwtICJGaXRuZXNzIgpnIDwtIHBsb3RUcmFpdEFyY2hpdGVjdHVyZShmb3VuZGVyUG9wLCBtZXRob2RUeXBlKQpnZ3Bsb3QyOjpnZ3NhdmUoZmlsZW5hbWUgPSBwYXN0ZTAoInRyYWl0YXJjaGl0ZWN0dXJlXyIsIG1ldGhvZFR5cGUsICIucGRmIiksCiAgICAgICAgICAgICAgICBwYXRoPXNhdmVfZGlyLAogICAgICAgICAgICAgICAgZGV2aWNlID0gInBkZiIpCgpwIDwtIHBsb3QzZFBvcHVsYXRpb25GaXRuZXNzKGZvdW5kZXJQb3AsIGNhbGN1bGF0ZUZpdG5lc3NUd29UcmFpdCkKCmZuYW1lIDwtIGZpbGUucGF0aChzYXZlX2RpciwgIjNkcG9wdWxhdGlvbmZpdG5lc3MuaHRtbCIpCmh0bWx3aWRnZXRzOjpzYXZlV2lkZ2V0KGFzX3dpZGdldChwKSwgZm5hbWUpCmBgYAoKU2ltdWxhdGUgc2V2ZXJhbCBhZGFwdGl2ZSB3YWxrcyB3aXRoIGRpZmZlcmVudCBwb3B1bGF0aW9uIHNpemVzCmBgYHtyfQojIERpZmZlcmVudCBzdGFydGluZyBwb3B1bGF0aW9uIHNpemVzCm4ucG9wU2l6ZXMgPC0gYyg0MCwyMDAsMTAwMCkKCiMgRG9uJ3QgdXNlIGEgU05QIGNoaXAgZm9yIHRoZXNlIHNpbXVsYXRpb25zCmFkZFNucENoaXAgPC0gRkFMU0UKCmZpZyA8LSBwbG90X2x5KCkKZml0X2RmIDwtIGRhdGEuZnJhbWUoZ2VuPTE6bi5nZW5zLAogICAgICAgICAgICAgICAgICAgZml0bmVzcz1udW1lcmljKG4uZ2VucyksCiAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEE9bnVtZXJpYyhuLmdlbnMpLAogICAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW51bWVyaWMobi5nZW5zKSkKCiMgSXRlcmF0ZSB0aHJvdWdoIGVhY2ggcG9wdWxhdGlvbiBzaXplCmZvciAocCBpbiBjKDE6bGVuZ3RoKG4ucG9wU2l6ZXMpKSkgewogIG4ucG9wU2l6ZSA8LSBuLnBvcFNpemVzW3BdCiAgcHJpbnQobi5wb3BTaXplKQogICMgQ3JlYXRlIGEgbmV3IHBvcHVsYXRpb24gb2YgdGhhdCBzaXplCiAgc291cmNlKCJTY3JpcHRzL0NyZWF0ZUZvdW5kZXJQb3AuUiIpCiAgcG9wIDwtIGZvdW5kZXJQb3AKICAjIEl0ZXJhdGUgdGhyb3VnaCB0aGUgZ2VuZXJhdGlvbnMsIHVwZGF0ZSB0aGUgcmVzdWx0IGRhdGFmcmFtZSwgYW5kIGFkdmFuY2UKICAjIHByb2dlbnkgYmFzZWQgb24gdGhlIHR3byB0cmFpdCBmaXRuZXNzIGZ1bmNpb24KICBmb3IoZ2VuIGluIDE6bi5nZW5zKSB7CiAgICBmaXRfZGYkZml0bmVzc1tnZW5dIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKGd2KHBvcCkpKQogICAgZml0X2RmJHRyYWl0VmFsQVtnZW5dIDwtIG1lYW5HKHBvcClbMV0KICAgIGZpdF9kZiR0cmFpdFZhbEJbZ2VuXSA8LSBtZWFuRyhwb3ApWzJdCiAgICBtZWFuRml0bmVzcyA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3ApKSkKICAgIHNlbFJhdCA8LSBzZWxlY3Rpb25SYXRpbyhtZWFuRml0bmVzcykKICAgIHBvcCA8LSBzZWxlY3RDcm9zcyhwb3AsIHRyYWl0PXR3b1RyYWl0Rml0RnVuYywgbkluZD1uLnBvcFNpemUqc2VsUmF0LCBuQ3Jvc3Nlcz1uLnBvcFNpemUpCiAgfQogICMgQWRkIGEgdHJhY2UgdG8gcmVwcmVzZW50IHRoaXMgcG9wdWxhdGlvbidzIGFkYXB0aXZlIHdhbGsKICBmaWcgPC0gYWRkX3RyYWNlKAogICAgZmlnLAogICAgZml0X2RmLAogICAgbmFtZSA9IG4ucG9wU2l6ZSwKICAgIHggPSBmaXRfZGYkdHJhaXRWYWxBLAogICAgeSA9IGZpdF9kZiR0cmFpdFZhbEIsCiAgICB6ID0gZml0X2RmJGZpdG5lc3MsCiAgICB0eXBlID0gJ3NjYXR0ZXIzZCcsCiAgICBtb2RlID0gJ2xpbmVzJywKICAgIG9wYWNpdHkgPSAxLAogICAgY29sb3IgPSBwLAogICAgbGluZSA9IGxpc3Qod2lkdGggPSA1KQogICkKICAKICAKfQoKZmlnIDwtIGZpZyAlPiUKICBsYXlvdXQobGVnZW5kPWxpc3QodGl0bGU9bGlzdCh0ZXh0PSdQb3B1bGF0aW9uIFNpemUnKSksCiAgICAgICAgIHNjZW5lID0gbGlzdCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgQSIpLAogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlRyYWl0IEIiKSwKICAgICAgICAgICAgICAgICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJGaXRuZXNzIiksCiAgICAgICAgICAgICAgICAgICAgICBhc3BlY3Rtb2RlPSdjdWJlJykpICU+JSBoaWRlX2NvbG9yYmFyKCkKCgpzYXZlX2RpciA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIkRpZmZlcmVudFBvcHVsYXRpb25TaXplcyIpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCnNhdmVfZGlyIDwtIGZpbGUucGF0aChzYXZlX2RpciwgZm9ybWF0KFN5cy50aW1lKCksICIlRl8lSF8lTV8lUyIpKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQp3cml0ZS50YWJsZShnZXRQYXJhbXMoKSwgZmlsZS5wYXRoKHNhdmVfZGlyLCAicGFyYW1zLnR4dCIpLCBjb2wubmFtZXM9RkFMU0UsIHF1b3RlPUZBTFNFLCBzZXA9IjpcdCIpCgpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsIHBhc3RlMCgicG9wdWxhdGlvblNpemVzXyIsIG4ucG9wU2l6ZXNbMV0sICIsIiwgbi5wb3BTaXplc1syXSwgIiwiLCBuLnBvcFNpemVzWzNdLCAiLmh0bWwiKSkKaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KGZpZyksIGZuYW1lKSAgCmBgYAoKT3ZlcmxheSBhbiBhZGFwdGl2ZSB3YWxrIG92ZXIgYSBmaXRuZXNzIGxhbmRzY2FwZSwgYW5kIHNhdmUgM0QgYW5kIDJEIHZlcnNpb25zCmBgYHtyfQpmaWcgPC0gcGxvdF9seSgpCmZpdF9kZiA8LSBkYXRhLmZyYW1lKGdlbj0xOm4uZ2VucywKICAgICAgICAgICAgICAgICAgIGZpdG5lc3M9bnVtZXJpYyhuLmdlbnMpLAogICAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPW51bWVyaWMobi5nZW5zKSwKICAgICAgICAgICAgICAgICAgIHRyYWl0VmFsQj1udW1lcmljKG4uZ2VucykpCgpwb3AgPC0gZm91bmRlclBvcAoKZm9yKGdlbiBpbiAxOm4uZ2VucykgewogIGZpdF9kZiRmaXRuZXNzW2dlbl0gPC0gbWVhbih0d29UcmFpdEZpdEZ1bmMoZ3YocG9wKSkpCiAgZml0X2RmJHRyYWl0VmFsQVtnZW5dIDwtIG1lYW5HKHBvcClbMV0KICBmaXRfZGYkdHJhaXRWYWxCW2dlbl0gPC0gbWVhbkcocG9wKVsyXQogIG1lYW5GaXRuZXNzIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcCkpKQogIHNlbFJhdCA8LSBzZWxlY3Rpb25SYXRpbyhtZWFuRml0bmVzcykKICBwb3AgPC0gc2VsZWN0Q3Jvc3MocG9wLCB0cmFpdD10d29UcmFpdEZpdEZ1bmMsIG5JbmQ9bi5wb3BTaXplKnNlbFJhdCwgbkNyb3NzZXM9bi5wb3BTaXplKQp9CgpzYXZlX2RpciA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIk92ZXJsYXlBZGFwdGl2ZVdhbGsiKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQpzYXZlX2RpciA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsIGZvcm1hdChTeXMudGltZSgpLCAiJUZfJUhfJU1fJVMiKSkKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKCnBsb3RUeXBlIDwtICJDT05UT1VSIgpwYyA8LSBvdmVybGF5V2Fsa09uTGFuZHNjYXBlKGZpdF9kZiwgdHlwZT1wbG90VHlwZSwgY2FsY3VsYXRlRml0bmVzc1R3b1RyYWl0KQpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsIHBhc3RlMCgiYWRhcHRpdmV3YWxrXyIsIHBsb3RUeXBlLCAiLmh0bWwiKSkKaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KHBjKSwgZm5hbWUpCgpwbG90VHlwZSA8LSAiU1VSRkFDRSIKcHMgPC0gb3ZlcmxheVdhbGtPbkxhbmRzY2FwZShmaXRfZGYsIHR5cGU9cGxvdFR5cGUsIGNhbGN1bGF0ZUZpdG5lc3NUd29UcmFpdCkKZm5hbWUgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCBwYXN0ZTAoImFkYXB0aXZld2Fsa18iLCBwbG90VHlwZSwgIi5odG1sIikpCmh0bWx3aWRnZXRzOjpzYXZlV2lkZ2V0KGFzX3dpZGdldChwcyksIGZuYW1lKQoKd3JpdGUudGFibGUoZ2V0UGFyYW1zKCksIGZpbGUucGF0aChzYXZlX2RpciwgInBhcmFtcy50eHQiKSwgY29sLm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSwgc2VwPSI6XHQiKQpgYGAKClBsb3QgdGhlIGNoYW5nZSBpbiBhbGxlbGUgZnJlcXVlbmN5IG92ZXIgdGltZQpgYGB7cn0Kbi5xdGxQZXJDaHIgPC0gMgpzb3VyY2UoIlNjcmlwdHMvQ3JlYXRlRm91bmRlclBvcC5SIikKcG9wIDwtIGZvdW5kZXJQb3AKCiMgUmVzdWx0cyBkYXRhZnJhbWUKZnJlcS5kZiA8LSBkYXRhLmZyYW1lKGdlbj0xOm4uZ2VucykKCiMgR2V0IHRoZSBlZmZlY3Qgc2l6ZXMgb2YgZWFjaCBxdGwKcXRsRWZmLmRmIDwtIGdldFF0bEVmZmVjdFNpemVzKHBvcCkKCiMgR2V0IHRoZSBuYW1lcyBvZiBhbGwgdGhlIFFUTHMKcXRsIDwtIHJvd25hbWVzKHF0bEVmZi5kZikKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lIG9mIGFsbCB6ZXJvcyB3aGVyZSB0aGUgY29sdW1ucyBhcmUgdGhlIFFUTCBpZHMsIGFuZCB0aGUgIyByb3dzIGlzIHRoZSAjIG9mIGdlbmVyYXRpb25zCnF0bC5kZiA8LSBkYXRhLmZyYW1lKG1hdHJpeCgwLCBuY29sPWxlbmd0aChxdGwpLCBucm93PW4uZ2VucykpCmNvbG5hbWVzKHF0bC5kZikgPC0gcXRsCgojIENvbWJpbmUgdGhlIGRhdGFmcmFtZXMKZnJlcS5kZiA8LSBjYmluZChmcmVxLmRmLCBxdGwuZGYpCgojIEl0ZXJhdGUgdGhyb3VnaCBlYWNoIGdlbmVyYXRpb24KZm9yKGdlbiBpbiAxOm4uZ2VucykgewogIG1lYW5GaXRuZXNzIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcCkpKQogICMgR2V0IHRoZSBxdGwgZ2Vub3R5cGUgZGF0YQogIHF0bEdlbm8gPC0gZ2V0VW5pcXVlUXRsKHBvcCkKICAjIEdldCB0aGUgZnJlcXVlbmN5IG9mIHRoZSAnMicgYWxsZWxlIGF0IGVhY2ggbG9jdXMKICBmb3IgKGwgaW4gMTpsZW5ndGgocXRsKSkgewogICAgIyBpZCBpcyB0aGUgbmFtZSBvZiB0aGUgcXRsIChjaHJfc2l0ZSkKICAgIGlkIDwtIHF0bFtsXQogICAgIyBBIGxpc3Qgb2YgZ2Vub3R5cGUgZGF0YSBmb3IgZWFjaCBpbmRpdmlkdWFsIGluIHRoZSBwb3B1bGF0aW9uIGF0IHRoYXQgbG9jdXMKICAgIGxvY3VzIDwtIHF0bEdlbm9bLGxdCiAgICAjIENhbGN1bGF0ZSB0aGUgYWxsZWxlIGZyZXF1ZW5jeSBhcyB0aGUgZnJlcXVlbmN5IG9mIGhvbW96eWdvdXMgaW5kaXZpZHVhbHMgKGZvciAnYWxsZWxlJykgKwogICAgIyAxLzIgKiBmcmVxdWVuY3kgb2YgaGV0ZXJvenlnb3VzIGluZGl2aWR1YWxzIChhc3N1bWVzIHRoZSBsb2N1cyBpcyBiaWFsbGVsaWMpCiAgICBmcmVxLmRmW2dlbixpZF0gPC0gKHN1bShsb2N1cz09bi5hbGxlbGUpL24ucG9wU2l6ZSkgKyAoKHN1bShsb2N1cz09MSkvbi5wb3BTaXplKS8yKQogIH0KICAjIERldGVybWluZSBzZWxlY3Rpb24gcmF0aW9uIGJhc2VkIG9uIGZpdG5lc3MKICBzZWxSYXQgPC0gc2VsZWN0aW9uUmF0aW8obWVhbkZpdG5lc3MpCiAgI0FkdmFuY2UgaW5kaXZpZHVhbHMKICBwb3AgPC0gc2VsZWN0Q3Jvc3MocG9wLCB0cmFpdD10d29UcmFpdEZpdEZ1bmMsIG5JbmQ9bi5wb3BTaXplKnNlbFJhdCwgbkNyb3NzZXM9bi5wb3BTaXplKQp9CgojIE1ha2UgdGhlIGRhdGFmcmFtZSB0aWR5CmZyZXEuZGY8LSBtZWx0KGZyZXEuZGYsIGlkPSJnZW4iLCB2YXJpYWJsZS5uYW1lPSJRVEwgSUQiLCB2YWx1ZS5uYW1lPSJBbGxlbGUgRnJlcXVlbmN5IikKCiMgQWRkIHRoZSBxdGwgZWZmZWN0IHNpemUgZGF0YSB0byB0aGUgZGF0YWZyYW1lCmZyZXEuZGYgPC0gbWVyZ2UoZnJlcS5kZiwgcXRsRWZmLmRmLCBieS54PSJRVEwgSUQiLCBieS55PSJyb3cubmFtZXMiLCBhbGwueD1UUlVFKQoKIyBDcmVhdGUgYSBsaW5lIHBsb3QgZm9yIHRoZSBjaGFuZ2UgaW4gZnJlcXVlbmN5IG9mIGFsbGVsZXMgb3ZlciB0aW1lCiMgRWFjaCBsaW5lJ3Mgb3BhY2l0eSBpcyBhIGZ1bmN0aW9uIG9mIGl0cyBlZmZlY3Qgc2l6ZSAoaGlnaGVyPWRhcmtlcikKZyA8LSBnZ3Bsb3QoZnJlcS5kZiwgYWVzKHg9Z2VuLCB5PWZyZXEsIGNvbG9yPWlkLCBhbHBoYT1lZmZfc2l6ZSkpICsKICBnZW9tX2xpbmUoc2l6ZT0wLjcpCgpzYXZlX2RpciA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIkFsbGVsZUZyZXF1ZW5jaWVzIikKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCBmb3JtYXQoU3lzLnRpbWUoKSwgIiVGXyVIXyVNXyVTIikpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCndyaXRlLnRhYmxlKGdldFBhcmFtcygpLCBmaWxlLnBhdGgoc2F2ZV9kaXIsICJwYXJhbXMudHh0IiksIGNvbC5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UsIHNlcD0iOlx0IikKCmdncGxvdDI6Omdnc2F2ZShmaWxlbmFtZSA9IHBhc3RlMCgiYWxsZWxlZnJlcXVlbmNpZXMucGRmIiksCiAgICAgICAgICAgICAgICBwYXRoPXNhdmVfZGlyLAogICAgICAgICAgICAgICAgZGV2aWNlID0gInBkZiIsCiAgICAgICAgICAgICAgICB3aWR0aD0xMCwKICAgICAgICAgICAgICAgIGhlaWdodD03KQpgYGAKClNpbXVsYXRlIGFuIGFkYXB0aXZlIHdhbGssIGFuZCBwbG90IHRoZSBmb2xsb3dpbmcgY3VydmU6CiNzZWdyZWdhdGluZyBRVEwvI3NlZ3JlZ2F0aW5nIGxvY2kKCk9yaWdpbmFsIHF1ZXN0aW9uOiBvdXQgb2Ygc2VncmVnYXRpbmcgYWxsZWxlcyBsZWZ0IGluIHRoZSBwb3B1bGF0aW9uLCBob3cgbWFueQpvZiB0aGVtIHdvdWxkIGluY3JlYXNlIGZpdG5lc3M/IElzIHRoaXMgZXZlbiBhIHZhbGlkIHF1ZXN0aW9uPwoKVE9ETzoKLSBIb3cgZG9lcyB0aGlzIGNoYW5nZSB3aXRoIHNpemUgb2YgYWxsZWxlPwotIEhvdyBkb2VzIHNpemUgb2YgJ2ZpdG5lc3MgZGVsdGEnIGNoYW5nZSBvdmVyIGdlbmVyYXRpb25zPwotIFNpbXVhbGF0ZSBtdXRhdGlvbnMgZm9yIGEgc2luZ2xlIGluZGl2aWR1YWwgdG8gZmlndXJlIG91dCBQKGFsbGVsaWMgc3Vic3RpdHV0aW9uIGlzIGZhdm9yYWJsZSksCnNob3VsZCByZWZsZWN0IEZLTwotIFAobXV0YXRpb24gZ2V0cyBmaXhlZCkgLSBzaG93IHRoYXQgdGhpcyBtYXRjaGVzIEtpbXVyYQotIENvbXBhcmUgYWRhcHRpdmUgd2Fsa3MgZm9yIERIL2luYnJlZCBwb3B1bGF0aW9uIHdoZXJlIHRoZXJlIGFyZSBuZXcgbXV0YXRpb25zIFZTCnBvcHVsYXRpb24gdy9zdGFuZGluZyBnZW5ldGljIHZhcmlhdGlvbgpgYGB7cn0Kbi5nZW5zIDwtIDIwMAoKIyBSZXNldCB2YXJpYWJsZXMKc291cmNlKCJTY3JpcHRzL0NyZWF0ZUZvdW5kZXJQb3AuUiIpCnNvdXJjZSgiU2NyaXB0cy9HbG9iYWxWYXJpYWJsZXMuUiIpCgpwb3AgPC0gZm91bmRlclBvcApkZiA8LSBkYXRhLmZyYW1lKGdlbj1jKCksCiAgICAgICAgICAgICAgICAgZml0bmVzcz1jKCksCiAgICAgICAgICAgICAgICAgdHJhaXRWYWwxPWMoKSwKICAgICAgICAgICAgICAgICB0cmFpdFZhbDI9YygpLAogICAgICAgICAgICAgICAgIHBlcmNGaXRJbmM9YygpLAogICAgICAgICAgICAgICAgIHNlZ0FsbGVsZXM9YygpLAogICAgICAgICAgICAgICAgIHNlZ1F0bD1jKCkpCgojIEl0ZXJhdGUgdGhyb3VnaCB0aGUgZ2VuZXJhdGlvbnMKZm9yIChnZW4gaW4gMTpuLmdlbnMpIHsKICBwcmludChnZW4pCiAgIyBDb3VudGVyIHZhcmlhYmxlcwogIG51bUhldExvY2kgPSAwCiAgbnVtSGV0UXRsID0gMAogIG51bUluYyA9IDAKCiAgIyBHZXQgYWxsIG9mIHRoZSBsb2NpIGZyb20gdGhlIHBvcHVsYXRpb24KICBzZWdTaXRlR2VubyA8LSBwdWxsU2VnU2l0ZUdlbm8ocG9wKQogICMgR2V0IGFsbCBvZiB0aGUgUVRMIGZyb20gdGhlIHBvcHVsYXRpb24gKGEgc2FtcGxpbmcgb2YgdGhlIHNlZ1NpdGVHZW5vKQogIHF0bEdlbm8gPC0gZ2V0VW5pcXVlUXRsKHBvcCkKICBxdGxMb2NpIDwtIGNvbG5hbWVzKHF0bEdlbm8pCiAgbG9jaSA8LSBjb2xuYW1lcyhzZWdTaXRlR2VubykKICBuTG9jaSA8LSBuY29sKHNlZ1NpdGVHZW5vKQogICMgSXRlcmF0ZSB0aHJvdWdoIGFsbCBvZiB0aGUgbG9jaQogICMgVE9ETzoganVzdCBjYWxjdWxhdGUgbnVtSGV0UXRsIC8gbnVtSGV0TG9jaQogIGZvciAobCBpbiAxOm5jb2woc2VnU2l0ZUdlbm8pKSB7CiAgICBpZCA8LSBsb2NpW2xdCiAgICBsb2N1cyA9IHNlZ1NpdGVHZW5vWyxsXQogICAgIyBDaGVjayBpZiB0aGUgbG9jdXMgaXMgc2VncmVnYXRpbmcKICAgIGlmIChoZXRMb2N1cyhsb2N1cykpIHsKICAgICAgIyBJbmNyZW1lbnQgdGhlIGNvdW50ZXIKICAgICAgbnVtSGV0TG9jaSA8LSBudW1IZXRMb2NpICsgMQogICAgICAjIERldGVybWluZSB0aGUgZWZmZWN0IHNpemUgLSB0aGlzIGlzIHdoZXJlIHRoZSBidWcgaXMKICAgICAgI2UgPC0gZ2V0RWZmZWN0U2l6ZShsb2N1cywgaWQsIHBvcCwgIkZpdG5lc3MiKQogICAgICAjaWYgKGUgPiAwKSB7CiAgICAgICMgIG51bUluYyA8LSBudW1JbmMgKzEKICAgICAgI30KICAgICAgIyBJZiB0aGUgbG9jdXMgaXMgYSBRVEwsIHRoZW4gaXQgaGFzIGFuIGVmZmVjdCBzaXplCiAgICAgIGlmIChpZCAlaW4lIHF0bExvY2kpIHsKICAgICAgICBudW1IZXRRdGwgPC0gbnVtSGV0UXRsICsgMQogICAgICB9CiAgICB9CiAgfQoKICAjIENhbGN1bGF0ZSB0aGUgcmF0aW8gb2Ygc2VncmVnYXRpbmcgUVRMIHRvIHNlZ3JlZ2F0aW5nIGFsbGVsZXMKICBpZiAobnVtSGV0TG9jaSA9PSAwKSB7CiAgICBwZXJjIDwtIDAKICB9IGVsc2UgewogICAgcGVyYyA8LSAobnVtSGV0UXRsIC8gbnVtSGV0TG9jaSkKICB9CiAgZGYgPC0gcmJpbmQoZGYsIGRhdGEuZnJhbWUoZ2VuPWdlbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaXRuZXNzPW1lYW4odHdvVHJhaXRGaXRGdW5jKGd2KHBvcCkpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbDE9bWVhbkcocG9wKVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbDI9bWVhbkcocG9wKVsyXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwZXJjRml0SW5jPXBlcmMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VnQWxsZWxlcz1udW1IZXRMb2NpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZ1F0bD1udW1IZXRRdGwpKQogICMgSWYgdGhlIHBvcHVsYXRpb24gaXMgd2l0aGluIG4ubWFyZ2luLCB0ZXJtaW5hdGUgdGhlIHNpbXVsYXRpb24KICBpZiAobWVhbih0d29UcmFpdEZpdEZ1bmMoZ3YocG9wKSkpID49IG4ubWFyZ2luKSB7CiAgICBicmVhawogIH0KICBtZWFuRml0bmVzcyA8LSBtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3ApKSkKICBzZWxSYXQgPC0gc2VsZWN0aW9uUmF0aW8obWVhbkZpdG5lc3MpCiAgIyBBZHZhbmNlIHRoZSB0b3AgcHJvZ2VueSBhY2NvcmRpbmcgdG8gdGhlIGZpdG5lc3MgZnVuY3Rpb24KICBwb3AgPC0gc2VsZWN0Q3Jvc3MocG9wPXBvcCwgdHJhaXQ9dHdvVHJhaXRGaXRGdW5jLCBuSW5kPW5JbmQocG9wKSpzZWxSYXQsIG5Dcm9zc2VzPW5JbmQocG9wKSkKfQoKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKG91dHB1dF9kaXIsICJCZW5lZmljaWFsQWxsZWxlcyIpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCnNhdmVfZGlyIDwtIGZpbGUucGF0aChzYXZlX2RpciwgZm9ybWF0KFN5cy50aW1lKCksICIlRl8lSF8lTV8lUyIpKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQp3cml0ZS50YWJsZShnZXRQYXJhbXMoKSwgZmlsZS5wYXRoKHNhdmVfZGlyLCAicGFyYW1zLnR4dCIpLCBjb2wubmFtZXM9RkFMU0UsIHF1b3RlPUZBTFNFLCBzZXA9IjpcdCIpCgpmbmFtZSA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsICJiZW5lZmljaWFsYWxsZWxlcy5wZGYiKQpwZGYoZm5hbWUpCgojIFBsb3QgdGhlIHJhdGlvIG9mIHNlZ3JlZ2F0aW5nIFFUTCB0byBzZWdyZWdhdGluZyBhbGxlbGVzLCBhbG9uZyB3aXRoIGZpdG5lc3MKcGFyKG1hciA9IGMoNSwgNSwgMywgNSkgKyAwLjMpCnBsb3QoZGYkZ2VuLCBkZiRmaXRuZXNzLCB0eXBlPSJsIiwgbHdkID0gIjMiLCBjb2w9MiwgeGxhYiA9ICJHZW5lcmF0aW9uIiwgeWxhYiA9ICJGaXRuZXNzIikKcGFyKG5ldyA9IFRSVUUpIApwbG90KGRmJGdlbiwgZGYkcGVyY0ZpdEluYywgdHlwZT0ibCIsIGx3ZCA9ICIzIiwgY29sID0gMywgYXhlcyA9IEZBTFNFLCB4bGFiID0gIiIsIHlsYWIgPSAiIikgCmF4aXMoc2lkZSA9IDQsIGF0ID0gcHJldHR5KHJhbmdlKGRmJHBlcmNGaXRJbmMpKSkKbXRleHQoIiUgb2YgU2VncmVnYXRpbmcgYWxsZWxlcyB0aGF0IGFyZSBRVEwiLCBzaWRlID0gNCwgbGluZSA9IDMpCnBhcih4cGQ9VFJVRSkKbGVnZW5kKCJyaWdodCIsCiAgYygiRml0bmVzcyIsICIlIEFsbGVsZXMiKSwKICAgICAgIGx0eSA9IDEsCiAgICAgICBsd2QgPSAzLAogICAgICAgY29sID0gMjozKQpkZXYub2ZmKCkKYGBgCgpUaGlzIGJsb2NrIHJ1bnMgbi5uUG9wcyAqIG4uc2ltcyBNb250ZSBDYXJsbyBzaW11bGF0aW9ucyBvZiBkaWZmZXJlbnQgcG9wdWxhdGlvbnMgdG8gZGV0ZXJtaW5lCnRoZSBjaGFuZ2UgaW4gdGhlIGF2ZXJhZ2UgZWZmZWN0IHNpemUgb2YgYWxsZWxlcyB0aGF0IGFyZSBmaXhlZCBhbG9uZyB0aGUgYWRhcHRpdmUgd2FsaywKc2F2aW5nIHRoZSBvcmRlciBpbiB3aGljaCB0aGUgYWxsZWxlcyBhcmUgZml4ZWQKbi5uUG9wcyBwb3B1bGF0aW9ucyBhcmUgY3JlYXRlZCwgYW5kIGVhY2ggb25lIHVuZGVyZ29lcyBuLnNpbXMgdW5pcXVlIGFkYXB0aXZlIHdhbGtzCgpSaWdodCBub3csIHdlIGRvIHNlZSBhbiB1cHdhcmQgY3VydmUgZm9yIHRoZSBhZGRpdGl2ZSBlZmZlY3QgY2hhcnQuIEEgZmF2b3JlZCBoeXBvdGhlc2lzOgotIE1vc3Qgb2YgdGhlIGFsbGVsZXMgaGF2ZSBhIHNtYWxsIGVmZmVjdCBzaXplLCBzbyBpdCBpcyBtb3JlIGxpa2VseSB0aGF0IHRoZXkgd291bGQgYmUgZml4ZWQKYGBge3J9Cm4uc2ltcyA8LSAxMApuLnBvcFJlc2V0cyA8LSAxMApuLnNlbFByb3AgPC0gMC4zCm4ucXRsUGVyQ2hyIDwtIDUKYWRkU25wQ2hpcCA8LSBGQUxTRQoKZmlnIDwtIHBsb3RfbHkoKQoKIyBUaWR5IGRhdGFmcmFtZSB0byBzdG9yZSB0aGUgb3JkZXIgaW4gd2hpY2ggZWFjaCBhbGxlbGUgaXMgZml4ZWQsIGFuZCB0aGUgZWZmZWN0IHNpemUKZWZmX3NpemVfZGYgPC0gZGF0YS5mcmFtZShvcmRlckZpeGVkPWMoKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBlZmZlY3RTaXplQT1jKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgZWZmZWN0U2l6ZUY9YygpKQojIENyZWF0ZSBhIG5ldyBwb3B1bGF0aW9uIG4ublBvcHMgdGltZXMKZm9yIChwIGluIDE6bi5wb3BSZXNldHMpIHsKICBwcmludChwYXN0ZTAoIlBvcCBSZXNldDogIiwgcCkpCiAgc291cmNlKCJTY3JpcHRzL0NyZWF0ZUZvdW5kZXJQb3AuUiIpCiAgZm9yIChzIGluIDE6bi5zaW1zKSB7CiAgICBwb3AgPC0gZm91bmRlclBvcAogICAgcG9wX2RmIDwtIGRhdGEuZnJhbWUoZ2VuPTE6bi5nZW5zLAogICAgICAgICAgICAgICAgICAgICAgICAgZml0bmVzcz1udW1lcmljKG4uZ2VucyksCiAgICAgICAgICAgICAgICAgICAgICAgICB0cmFpdFZhbEE9bnVtZXJpYyhuLmdlbnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW51bWVyaWMobi5nZW5zKSkKICAKICAgIHByaW50KHBhc3RlMCgiU2ltOiAiLCBzKSkKICAgICMgaWR4IGlzIHRoZSBvcmRlciBpbiB3aGljaCBhbiBhbGxlbGUgaXMgZml4ZWQgYWxvbmcgYW4gYWRhcHRpdmUgd2FsawogICAgaWR4IDwtIDEKICAgICMgd2hldGhlciBvciBub3QgdG8gaW5jcmVtZW50IHRoZSBpZHggY291bnRlci4gTXVsdGlwbGUgYWxsZWxlcyBtYXkgYmUgZml4ZWQKICAgICMgaW4gdGhlIHNhbWUgZ2VuZXJhdGlvbiwgc28gdGhpcyBjYW5ub3QgYmUgaW5jcmVtZW50ZWQgdW50aWwgZWFjaCBsb2N1cwogICAgIyBoYXMgYmVlbiBleGFtaW5lZAogICAgaW5jIDwtIEZBTFNFCiAgICAKICAgICMgQnVybi1pbgogICAgZm9yIChnZW4gaW4gMTpuLmJ1cm5JbkdlbnMpIHsKICAgICAgcG9wIDwtIHNlbGVjdENyb3NzKHBvcCwgdHJhaXQ9dHdvVHJhaXRGaXRGdW5jLCBuSW5kPW5JbmQocG9wKSpuLmJ1cm5JblNlbFByb3AsIG5Dcm9zc2VzPW5JbmQocG9wKSkKICAgIH0KICAgIAogICAgIyBNYWluIHNpbXVsYXRpb24KICAgIGZvciAoZ2VuIGluIDE6bi5nZW5zKSB7CiAgICAgIG1lYW5GaXRuZXNzIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcCkpKQogICAgICBzZWxSYXQgPC0gc2VsZWN0aW9uUmF0aW8obWVhbkZpdG5lc3MpCiAgICAgIHBvcF9kZiRmaXRuZXNzW2dlbl0gPC0gbWVhbkZpdG5lc3MKICAgICAgcG9wX2RmJHRyYWl0VmFsQVtnZW5dIDwtIG1lYW5QKHBvcClbMV0KICAgICAgcG9wX2RmJHRyYWl0VmFsQltnZW5dIDwtIG1lYW5QKHBvcClbMl0KICAgICAgIyBLZWVwIHRyYWNrIG9mIHRoZSBjdXJyZW50IHBvcHVsYXRpb24KICAgICAgcHJldlBvcCA8LSBwb3AKICAgICAgIyBBZHZhbmNlIHRoZSBwb3B1bGF0aW9uIGJhc2VkIG9uIGZpdG5lc3MKICAgICAgcG9wIDwtIHNlbGVjdENyb3NzKHBvcCwgdHJhaXQ9dHdvVHJhaXRGaXRGdW5jLCBuSW5kPW5JbmQocG9wKSpzZWxSYXQsIG5Dcm9zc2VzPW5JbmQocG9wKSkKICAgICAgIyBHZXQgdGhlIHF0bCBnZW5vdHlwZXMgZnJvbSB0aGUgY3VycmVudCBhbmQgbmV3IHBvcHVsYXRpb25zLCBzbyB3ZSBjYW4gY29tcGFyZSB0aGVtCiAgICAgIHByZXZHZW5vIDwtIGdldFVuaXF1ZVF0bChwcmV2UG9wKQogICAgICBuZXdHZW5vIDwtIGdldFVuaXF1ZVF0bChwb3ApCiAgICAgIGNvbHMgPC0gY29sbmFtZXMobmV3R2VubykKICAgICAgbi5sb2NpIDwtIGxlbmd0aChjb2xzKQogICAgICAjIEl0ZXJhdGUgdGhyb3VnaCBhbGwgbG9jaQogICAgICBmb3IgKGwgaW4gMTpuLmxvY2kpIHsKICAgICAgICBpZCA8LSBjb2xzW2xdCiAgICAgICAgcHJldkxvY3VzID0gcHJldkdlbm9bLGxdCiAgICAgICAgbmV3TG9jdXMgPSBuZXdHZW5vWyxsXQogICAgICAgICMgQ2hlY2sgaWYgdGhlIGFsbGVsZSB3YXMgc2VncmVnYXRpbmcgYW5kIGJlY2FtZSBmaXhlZCBpbiB0aGlzIGdlbmVyYXRpb24KICAgICAgICBpZiAoaGV0TG9jdXMocHJldkxvY3VzKSAmJiAhaGV0TG9jdXMobmV3TG9jdXMpKSB7CiAgICAgICAgICAjIEluY3JlbWVudCB0aGUgb3JkZXIgY291bnRlciBhZnRlciB0aGlzIGdlbmVyYXRpb24KICAgICAgICAgIGluYyA8LSBUUlVFCiAgICAgICAgICAjIERldGVybWluZSB0aGUgZWZmZWN0IHNpemUsIGJhc2VkIG9uIGFkZGl0aXZpdHkKICAgICAgICAgIGVmZlNpemVBIDwtIGdldEVmZmVjdFNpemUocHJldkxvY3VzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByZXZQb3AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFkZGl0aXZlIikKICAgICAgICAgICNwcmludChwYXN0ZTAoIkdlbjogIiwgZ2VuLCAiLCBPcmRlcjogIiwgaWR4LCAiLCBMb2M6ICIsIGlkLCAiOiAiLCBlZmZTaXplQSkpICMgUkVNT1ZFCiAgICAgICAgICAjIERldGVybWluZSB0aGUgZWZmZWN0IHNpemUgYmFzZWQgb24gZml0bmVzcwogICAgICAgICAgZWZmU2l6ZUYgPC0gZ2V0RWZmZWN0U2l6ZShwcmV2TG9jdXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcmV2UG9wLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRml0bmVzcyIpCiAgICAgICAgICAjIFVwZGF0ZSB0aGUgcmVzdWx0IGRhdGFmcmFtZQogICAgICAgICAgbmV3X3JvdyA8LSBkYXRhLmZyYW1lKG9yZGVyRml4ZWQ9YyhpZHgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdFNpemVBPWMoZWZmU2l6ZUEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVmZmVjdFNpemVGPWMoZWZmU2l6ZUYpKQogICAgICAgICAgZWZmX3NpemVfZGYgPC0gcmJpbmQoZWZmX3NpemVfZGYsIG5ld19yb3cpCiAgICAgICAgfQogICAgICB9CiAgICAgIAogICAgICAjIENoZWNrIHdoZXRoZXIgdG8gaW5jcmVtZW50IHRoZSBvcmRlciBjb3VudGVyIGFuZCByZXNldCAnaW5jJwogICAgICBpZiAoaW5jKSB7CiAgICAgICAgaWR4IDwtIGlkeCArIDEKICAgICAgICBpbmMgPC0gRkFMU0UKICAgICAgfQogICAgICAjIElmIHRoZSBwb3B1bGF0aW9uIGlzIHdpdGhpbiBuLm1hcmdpbiwgdGVybWluYXRlIHRoZSBzaW11bGF0aW9uCiAgICAgIGlmIChtZWFuKHR3b1RyYWl0Rml0RnVuYyhndihzdWJQb3ApKSkgPj0gbi5tYXJnaW4pIHsKICAgICAgICBicmVhawogICAgICB9CiAgICAgIAogICAgfQogICAgIyBBZGQgdGhlIGFkYXB0aXZlIHdhbGsgb2YgdGhlIHN1Yi1wb3B1bGF0aW9uCiAgICBmaWcgPC0gYWRkX3RyYWNlKAogICAgICBmaWcsCiAgICAgIHBvcF9kZiwKICAgICAgbmFtZSA9IHMsCiAgICAgIHggPSBwb3BfZGYkdHJhaXRWYWxBLAogICAgICB5ID0gcG9wX2RmJHRyYWl0VmFsQiwKICAgICAgeiA9IHBvcF9kZiRmaXRuZXNzLAogICAgICB0eXBlID0gJ3NjYXR0ZXIzZCcsCiAgICAgIG1vZGUgPSAnbGluZXMnLAogICAgICBvcGFjaXR5ID0gMSwKICAgICAgY29sb3IgPSBzLAogICAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDIpCiAgICApCiAgfQp9CgpzYXZlX2RpciA8LSBmaWxlLnBhdGgob3V0cHV0X2RpciwgIkF2ZXJhZ2VFZmZlY3RTaXplIikKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKc2F2ZV9kaXIgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCBmb3JtYXQoU3lzLnRpbWUoKSwgIiVGXyVIXyVNXyVTIikpCmlmICghZGlyLmV4aXN0cyhzYXZlX2RpcikpIGRpci5jcmVhdGUoc2F2ZV9kaXIpCndyaXRlLnRhYmxlKGdldFBhcmFtcygpLCBmaWxlLnBhdGgoc2F2ZV9kaXIsICJwYXJhbXMudHh0IiksIGNvbC5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UsIHNlcD0iOlx0IikKCiMgRGV0ZXJtaW5lIHRoZSBhdmVyYWdlIGFkZGl0aXZlIGVmZmVjdCBzaXplIGF0IGVhY2ggJ3N0ZXAnCmdyb3VwZWRfZGZfYSA8LSBlZmZfc2l6ZV9kZiAlPiUKICBncm91cF9ieShvcmRlckZpeGVkKSAlPiUKICBzdW1tYXJpemUobWVhbkVmZmVjdFNpemUgPSBtZWFuKGVmZmVjdFNpemVBKSkKCmdfYSA8LSBnZ3Bsb3QoZGF0YT1ncm91cGVkX2RmX2EsIGFlcyh4PW9yZGVyRml4ZWQsIHk9bWVhbkVmZmVjdFNpemUpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKSArCiAgZ2VvbV9zbW9vdGgoZm9ybXVsYSA9IHkgfiBhXngpCgpnZ3Bsb3QyOjpnZ3NhdmUoZmlsZW5hbWUgPSAiYXZlcmFnZV9lZmZlY3Rfc2l6ZV9hZGRpdGl2ZS5wZGYiLAogICAgICAgICAgICAgICAgcGF0aD1zYXZlX2RpciwKICAgICAgICAgICAgICAgIGRldmljZSA9ICJwZGYiKQoKIyBEZXRlcm1pbmUgdGhlIGF2ZXJhZ2UgZml0bmVzcyBlZmZlY3Qgc2l6ZSBhdCBlYWNoICdzdGVwJwpncm91cGVkX2RmX2YgPC0gZWZmX3NpemVfZGYgJT4lCiAgZ3JvdXBfYnkob3JkZXJGaXhlZCkgJT4lCiAgc3VtbWFyaXplKG1lYW5FZmZlY3RTaXplID0gbWVhbihlZmZlY3RTaXplRikpCgpnX2YgPC0gZ2dwbG90KGRhdGE9Z3JvdXBlZF9kZl9mLCBhZXMoeD1vcmRlckZpeGVkLCB5PW1lYW5FZmZlY3RTaXplKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikgKwogIGdlb21fc21vb3RoKGZvcm11bGEgPSB5IH4gYV54KQoKZ2dwbG90Mjo6Z2dzYXZlKGZpbGVuYW1lID0gImF2ZXJhZ2VfZWZmZWN0X3NpemVfZml0bmVzcy5wZGYiLAogICAgICAgICAgICAgICAgcGF0aD1zYXZlX2RpciwKICAgICAgICAgICAgICAgIGRldmljZSA9ICJwZGYiKQoKIyBDcmVhdGUgYSBwbG90IHdpdGggdGhlIGFkYXB0aXZlIHdhbGtzCnAgPC0gZmlnICU+JQogIGxheW91dChsZWdlbmQ9bGlzdCh0aXRsZT1saXN0KHRleHQ9J1BvcHVsYXRpb24nKSksCiAgICAgICAgIHNob3dsZWdlbmQ9RkFMU0UsCiAgICAgICAgIHNjZW5lID0gbGlzdCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgQSIpLAogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlRyYWl0IEIiKSwKICAgICAgICAgICAgICAgICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJGaXRuZXNzIiksCiAgICAgICAgICAgICAgICAgICAgICBhc3BlY3Rtb2RlPSdjdWJlJykpICU+JSBoaWRlX2NvbG9yYmFyKCkKZm5hbWUgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCAiYWRhcHRpdmV3YWxrLmh0bWwiKQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChhc193aWRnZXQocCksIGZuYW1lKQpgYGAKClRoaXMgYmxvY2sgd2lsbCBzaW11bGF0ZSBvbmUgYmFzZSBwb3B1bGF0aW9uLCBmcm9tIHdoaWNoIHR3byBzdWItcG9wdWxhdGlvbnMgYXJlIHNlbGVjdGVkLCBhbmQgdW5kZXJnbyBwdXJpZnlpbmcgc2VsZWN0aW9uIGluZGVwZW5kZW50bHkuCmBgYHtyfQpzb3VyY2UoIlNjcmlwdHMvR2xvYmFsVmFyaWFibGVzLlIiKQpzb3VyY2UoIlNjcmlwdHMvQ3JlYXRlRm91bmRlclBvcC5SIikKCmZpdF9kZiA8LSBkYXRhLmZyYW1lKGdlbj0xOm4uYnVybkluR2VucywKICAgICAgICAgICAgICAgICBmaXRuZXNzPW51bWVyaWMobi5idXJuSW5HZW5zKSwKICAgICAgICAgICAgICAgICB0cmFpdFZhbEE9bnVtZXJpYyhuLmJ1cm5JbkdlbnMpLAogICAgICAgICAgICAgICAgIHRyYWl0VmFsQj1udW1lcmljKG4uYnVybkluR2VucykpCnBvcCA8LSBmb3VuZGVyUG9wCgojIEJ1cm4taW4gZ2VuZXJhdGlvbnMKZm9yIChnZW4gaW4gMTpuLmJ1cm5JbkdlbnMpIHsKICBmaXRfZGYkZml0bmVzc1tnZW5dIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcCkpKQogIGZpdF9kZiR0cmFpdFZhbEFbZ2VuXSA8LSBtZWFuUChwb3ApWzFdCiAgZml0X2RmJHRyYWl0VmFsQltnZW5dIDwtIG1lYW5QKHBvcClbMl0KICBwb3AgPC0gc2VsZWN0Q3Jvc3MocG9wLCB0cmFpdD10d29UcmFpdEZpdEZ1bmMsIG5JbmQ9bkluZChwb3ApKm4uYnVybkluU2VsUHJvcCwgbkNyb3NzZXM9bkluZChwb3ApKQp9CgojIENyZWF0ZSBhIHJhbmRvbSB2ZWN0b3Igb2Ygc2l6ZSBuLnBvcHMsIHdpdGggYSByYW5kb20gb3JkZXIgb2Ygc3ViLXBvcHVsYXRpb24gaWRzCnJhbmRWZWMgPC0gc2FtcGxlKHJlcChjKDE6bi5uUG9wcyksIHRpbWVzPW4ucG9wU2l6ZS9uLm5Qb3BzKSkKCiMgU2VsZWN0IGFsbCBvZiB0aGUgIjEiIGluZGV4ZWQgaW5kaXZpZHVhbHMKcG9wQSA8LSBzZWxlY3RJbmQocG9wLCB0cmFpdD1zZWxlY3RTdWJQb3AsIHNlbGVjdFRvcD1UUlVFLCBuSW5kPW4uc3ViUG9wU2l6ZSwgaWR4PTEsIHJhbmRWZWM9cmFuZFZlYykKIyBTZWxlY3QgYWxsIG9mIHRoZSAiMiIgaW5kZXhlZCBpbmRpdmlkdWFscwpwb3BCIDwtIHNlbGVjdEluZChwb3AsIHRyYWl0PXNlbGVjdFN1YlBvcCwgc2VsZWN0VG9wPVRSVUUsIG5JbmQ9bi5zdWJQb3BTaXplLCBpZHg9MiwgcmFuZFZlYz1yYW5kVmVjKQoKIyBDcmVhdGUgZGF0YWZyYW1lcyBmb3IgZWFjaCBzdWJwb3B1bGF0aW9uLCBpbml0aWFsaXppbmcgd2l0aCBjdXJyZW50IHZhbHVlcwpwb3BBX2RmIDwtIGRhdGEuZnJhbWUoZ2VuPWMoMSksCiAgICAgICAgICAgICAgICAgZml0bmVzcz1jKG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcEEpKSkpLAogICAgICAgICAgICAgICAgIHRyYWl0VmFsQT1jKG1lYW5QKHBvcEEpWzFdKSwKICAgICAgICAgICAgICAgICB0cmFpdFZhbEI9YyhtZWFuUChwb3BBKVsyXSkpCnBvcEJfZGYgPC0gZGF0YS5mcmFtZShnZW49YygxKSwKICAgICAgICAgICAgICAgICBmaXRuZXNzPWMobWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wQikpKSksCiAgICAgICAgICAgICAgICAgdHJhaXRWYWxBPWMobWVhblAocG9wQilbMV0pLAogICAgICAgICAgICAgICAgIHRyYWl0VmFsQj1jKG1lYW5QKHBvcEIpWzJdKSkKCiMgSXRlcmF0ZSB0aHJvdWdoIHRoZSBnZW5lcmF0aW9ucwpmb3IgKGdlbiBpbiAxOm4uZ2VucykgewogICMgSWYgcG9wQSBpcyB3aXRoaW4gdGhlIG1hcmdpbiBvZiB0aGUgZml0bmVzcyBvcHRpbXVtLCBkb24ndCBwcm9ncmVzcyBpdCBhbnkgZnVydGhlcgogIGlmIChtZWFuKHR3b1RyYWl0Rml0RnVuYyhwaGVubyhwb3BBKSkpIDwgbi5tYXJnaW4pIHsKICAgICMgQWR2YW5jZSB0aGUgcG9wdWxhdGlvbgogICAgbWVhbkZpdG5lc3MgPC0gbWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wQSkpKQogICAgIyBHZXQgYSBzZWxlY3Rpb24gcmF0aW8gYmFzZWQgb24gZml0bmVzcwogICAgc2VsUmF0IDwtIHNlbGVjdGlvblJhdGlvKG1lYW5GaXRuZXNzKQogICAgcG9wQSA8LSBzZWxlY3RDcm9zcyhwb3BBLCB0cmFpdD10d29UcmFpdEZpdEZ1bmMsIG5JbmQ9bkluZChwb3BBKSpzZWxSYXQsIG5Dcm9zc2VzPW5JbmQocG9wQSkpCiAgICAjIFVwZGF0ZSB0aGUgZGF0YWZyYW1lIHdpdGggbmV3IHZhbHVlcwogICAgcG9wQV9kZiA8LSByYmluZChwb3BBX2RmLCBkYXRhLmZyYW1lKGdlbj1nZW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpdG5lc3M9bWVhbkZpdG5lc3MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWl0VmFsQT1tZWFuUChwb3BBKVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW1lYW5QKHBvcEEpWzJdKSkKICAgIAogIH0KICAjIElmIHBvcEIgaXMgd2l0aGluIHRoZSBtYXJnaW4gb2YgdGhlIGZpdG5lc3Mgb3B0aW11bSwgZG9uJ3QgcHJvZ3Jlc3MgaXQgYW55IGZ1cnRoZXIKICBpZiAobWVhbih0d29UcmFpdEZpdEZ1bmMocGhlbm8ocG9wQikpKSA8IG4ubWFyZ2luKSB7CiAgICAjIElmIHBvcEEgaXMgd2l0aGluIHRoZSBtYXJnaW4gb2YgdGhlIGZpdG5lc3Mgb3B0aW11bSwgZG9uJ3QgcHJvZ3Jlc3MgaXQgYW55IGZ1cnRoZXIKICAgIG1lYW5GaXRuZXNzIDwtIG1lYW4odHdvVHJhaXRGaXRGdW5jKHBoZW5vKHBvcEIpKSkKICAgICMgR2V0IGEgc2VsZWN0aW9uIHJhdGlvIGJhc2VkIG9uIGZpdG5lcwogICAgc2VsUmF0IDwtIHNlbGVjdGlvblJhdGlvKG1lYW5GaXRuZXNzKQogICAgcG9wQiA8LSBzZWxlY3RDcm9zcyhwb3BCLCB0cmFpdD10d29UcmFpdEZpdEZ1bmMsIG5JbmQ9bkluZChwb3BCKSpzZWxSYXQsIG5Dcm9zc2VzPW5JbmQocG9wQikpCiAgICAjIFVwZGF0ZSB0aGUgZGF0YWZyYW1lIHdpdGggbmV3IHZhbHVlcwogICAgcG9wQl9kZiA8LSByYmluZChwb3BCX2RmLCBkYXRhLmZyYW1lKGdlbj1nZW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpdG5lc3M9bWVhbkZpdG5lc3MsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRyYWl0VmFsQT1tZWFuUChwb3BCKVsxXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJhaXRWYWxCPW1lYW5QKHBvcEIpWzJdKSkKICB9Cn0KIyBVcGRhdGUgcm93bmFtZXMKcm93bmFtZXMocG9wQV9kZikgPC0gMTpucm93KHBvcEFfZGYpCnJvd25hbWVzKHBvcEJfZGYpIDwtIDE6bnJvdyhwb3BCX2RmKQoKIyBQbG90IHRoZSBhZGFwdGl2ZSB3YWxrcwpmaWcgPC0gcGxvdF9seSgpCmZpZyA8LSBhZGRfdHJhY2UoCiAgZmlnLAogIGZpdF9kZiwKICBuYW1lID0gIkJ1cm4gSW4iLAogIHggPSBmaXRfZGYkdHJhaXRWYWxBLAogIHkgPSBmaXRfZGYkdHJhaXRWYWxCLAogIHogPSBmaXRfZGYkZml0bmVzcywKICB0eXBlID0gJ3NjYXR0ZXIzZCcsCiAgbW9kZSA9ICdsaW5lcycsCiAgb3BhY2l0eSA9IDEsCiAgY29sb3IgPSAneWVsbG93JywKICBsaW5lID0gbGlzdCh3aWR0aCA9IDUpCikKCmZpZyA8LSBhZGRfdHJhY2UoCiAgICBmaWcsCiAgICBwb3BBX2RmLAogICAgbmFtZSA9ICJQb3AgQSIsCiAgICB4ID0gcG9wQV9kZiR0cmFpdFZhbEEsCiAgICB5ID0gcG9wQV9kZiR0cmFpdFZhbEIsCiAgICB6ID0gcG9wQV9kZiRmaXRuZXNzLAogICAgdHlwZSA9ICdzY2F0dGVyM2QnLAogICAgbW9kZSA9ICdsaW5lcycsCiAgICBvcGFjaXR5ID0gMSwKICAgIGNvbG9yID0gJ3JlZCcsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUpCiAgKQoKZmlnIDwtIGFkZF90cmFjZSgKICAgIGZpZywKICAgIHBvcEJfZGYsCiAgICBuYW1lID0gIlBvcCBCIiwKICAgIHggPSBwb3BCX2RmJHRyYWl0VmFsQSwKICAgIHkgPSBwb3BCX2RmJHRyYWl0VmFsQiwKICAgIHogPSBwb3BCX2RmJGZpdG5lc3MsCiAgICB0eXBlID0gJ3NjYXR0ZXIzZCcsCiAgICBtb2RlID0gJ2xpbmVzJywKICAgIG9wYWNpdHkgPSAxLAogICAgY29sb3IgPSAnYmx1ZScsCiAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDUpCiAgKQoKCnAgPC0gZmlnICU+JQogIGxheW91dChsZWdlbmQ9bGlzdCh0aXRsZT1saXN0KHRleHQ9J1BvcHVsYXRpb24nKSksCiAgICAgICAgIHNob3dsZWdlbmQ9RkFMU0UsCiAgICAgICAgIHNjZW5lID0gbGlzdCh4YXhpcyA9IGxpc3QodGl0bGUgPSAiVHJhaXQgQSIpLAogICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIlRyYWl0IEIiKSwKICAgICAgICAgICAgICAgICAgICAgIHpheGlzID0gbGlzdCh0aXRsZSA9ICJGaXRuZXNzIiksCiAgICAgICAgICAgICAgICAgICAgICBhc3BlY3Rtb2RlPSdjdWJlJykpICU+JSBoaWRlX2NvbG9yYmFyKCkKCnNhdmVfZGlyIDwtIGZpbGUucGF0aChvdXRwdXRfZGlyLCAiRGl2ZXJnaW5nUG9wdWxhdGlvbnMiKQppZiAoIWRpci5leGlzdHMoc2F2ZV9kaXIpKSBkaXIuY3JlYXRlKHNhdmVfZGlyKQpzYXZlX2RpciA8LSBmaWxlLnBhdGgoc2F2ZV9kaXIsIGZvcm1hdChTeXMudGltZSgpLCAiJUZfJUhfJU1fJVMiKSkKaWYgKCFkaXIuZXhpc3RzKHNhdmVfZGlyKSkgZGlyLmNyZWF0ZShzYXZlX2RpcikKZm5hbWUgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCAiYWRhcHRpdmV3YWxrcy5odG1sIikKaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KHApLCBmbmFtZSkKCmZuYW1lIDwtIGZpbGUucGF0aChzYXZlX2RpciwgIjJQb3B1bGF0aW9uRml0bmVzcy5odG1sIikKcCA8LSBwbG90M2RQb3B1bGF0aW9uRml0bmVzc1R3b1BvcHMocG9wQSwgcG9wQikKaHRtbHdpZGdldHM6OnNhdmVXaWRnZXQoYXNfd2lkZ2V0KHApLCBmbmFtZSkKd3JpdGUudGFibGUoZ2V0UGFyYW1zKCksIGZpbGUucGF0aChzYXZlX2RpciwgInBhcmFtcy50eHQiKSwgY29sLm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSwgc2VwPSI6XHQiKQoKZm5hbWUgPC0gZmlsZS5wYXRoKHNhdmVfZGlyLCAidHJhaXRhcmNoaXRlY3R1cmUucGRmIikKcGRmKGZuYW1lKQoKcDEgPC0gcGxvdFRyYWl0QXJjaGl0ZWN0dXJlKHBvcEEsICJGaXRuZXNzIiwgInBvcEEiKQpwMiA8LSBwbG90VHJhaXRBcmNoaXRlY3R1cmUocG9wQiwgIkZpdG5lc3MiLCAicG9wQiIpCnAzIDwtIHBsb3RUcmFpdEFyY2hpdGVjdHVyZShwb3BBLCAiQWRkaXRpdmUiLCAicG9wQSIpCnA0IDwtIHBsb3RUcmFpdEFyY2hpdGVjdHVyZShwb3BCLCAiQWRkaXRpdmUiLCAicG9wQiIpCgoocDF8cDIpLyhwM3xwNCkKZGV2Lm9mZigpCgojIENyZWF0ZSBhIGRlbnNpdHkgcGxvdCBvZiB0cmFpdCAxCnRyYWl0MS5kZiA8LSBhcy5kYXRhLmZyYW1lKGNiaW5kKHBoZW5vKHBvcEEpWywxXSwgcGhlbm8ocG9wQilbLDFdKSkKY29sbmFtZXModHJhaXQxLmRmKSA8LSBjKCJwb3BBIiwgInBvcEIiKQp0cmFpdDEuZGYgPC0gdHJhaXQxLmRmICU+JQogIHBpdm90X2xvbmdlcihjKCJwb3BBIiwgInBvcEIiKSwgbmFtZXNfdG89InBvcCIsIHZhbHVlc190bz0icGhlbm8iKQp0MSA8LSBnZ3Bsb3QodHJhaXQxLmRmLCBhZXMocGhlbm8sIGZpbGw9cG9wLCBjb2xvcj1wb3ApKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhPTAuMSkgKwogIGxhYnModGl0bGU9IlRyYWl0IDEiKQoKIyBDcmVhdGUgYSBkZW5zaXR5IHBsb3Qgb2YgdHJhaXQgMgp0cmFpdDIuZGYgPC0gYXMuZGF0YS5mcmFtZShjYmluZChwaGVubyhwb3BBKVssMl0sIHBoZW5vKHBvcEIpWywyXSkpCmNvbG5hbWVzKHRyYWl0Mi5kZikgPC0gYygicG9wQSIsICJwb3BCIikKdHJhaXQyLmRmIDwtIHRyYWl0Mi5kZiAlPiUKICBwaXZvdF9sb25nZXIoYygicG9wQSIsICJwb3BCIiksIG5hbWVzX3RvPSJwb3AiLCB2YWx1ZXNfdG89InBoZW5vIikKdDIgPC0gZ2dwbG90KHRyYWl0Mi5kZiwgYWVzKHBoZW5vLCBmaWxsPXBvcCwgY29sb3I9cG9wKSkgKwogIGdlb21fZGVuc2l0eShhbHBoYT0wLjEpICsKICBsYWJzKHRpdGxlPSJUcmFpdCAyIikKCgoodDF8dDIpCmdncGxvdDI6Omdnc2F2ZShmaWxlbmFtZSA9ICJ0cmFpdF9kaXN0cmlidXRpb25zLnBkZiIsCiAgICAgICAgICAgICAgICBwYXRoPXNhdmVfZGlyLAogICAgICAgICAgICAgICAgZGV2aWNlID0gInBkZiIpCgoKYGBgCg==